package cn.dduan.fool.design.pattern.behavior.observe;

import java.util.ArrayList;
import java.util.List;

/**
 * 观察者模式
 * <p>
 * 定义：观察者模式又称为 发布-订阅模式，定义了对象之间一对多依赖关系，当目标对象(被观察者)的状态发生改变时，它的所有依赖者(观察者)都会收到通知
 * <p>
 * 优点：
 * 1、一个观察目标可以对应多个观察者，而这些观察者之间没有相互联系，所以能够根据需要增加和删除观察者，使得系统更易于扩展，符合开闭原则
 * 2、并且观察者模式让目标对象和观察者松耦合，虽然彼此不清楚对方的细节，但依然可以交互，目标对象只知道一个具体的观察者列表，但并不认识任何一个具体的观察者，它只知道他们都有一个共同的接口
 * <p>
 * 缺点：
 * 1、存在很多个被观察者的话，那么将需要花费一定时间通知所有的观察者
 * 2、如果观察者与被观察者之间存在循环依赖的话，那么可能导致系统崩溃
 * <p>
 * 通知模式：
 * 1、拉取模式：目标角色在发生变化后，仅仅告诉观察者角色“我变化了”，观察者角色如果想要知道具体的变化细节，则就要自己从目标角色的接口中得到
 * 2、推顺模式：目标角色发生变化时，通知观察者的同时，通过参数将变化的细节传递到观察者角色中去
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
public class ObservePattern {
    public static void main(String[] args) {
        ConcreteSubject concreteSubject = new ConcreteSubject();

        CurrentConditionsDisplay conditionsDisplay = new CurrentConditionsDisplay(concreteSubject);

        concreteSubject.setMeasurements(80, 65, 30.4f);
        concreteSubject.setMeasurements(82, 70, 29.2f);
        concreteSubject.setMeasurements(78, 78, 40.4f);

    }
}

/**
 * 抽象主题（被观察者）
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
interface Subject {
    /**
     * 注册观察者
     *
     * @param observer
     */
    public void registerObserver(Observer observer);

    /**
     * 删除观察者
     *
     * @param observer
     */
    public void removeOberver(Observer observer);

    /**
     * 当主题状态发生改变时，这个方法需要被调用，以通知所有观察者
     */
    public void notifyObserver();
}

/**
 * 具体实现
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
class ConcreteSubject implements Subject {
    private List<Observer> observers;
    private float tempterature;
    private float pressure;
    private float humidity;

    public ConcreteSubject() {
        observers = new ArrayList<Observer>();
    }

    @Override
    public void notifyObserver() {
        for (int i = 0; i < observers.size(); i++) {
            Observer observer = observers.get(i);
            observer.update(tempterature, humidity, pressure);
        }
    }

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeOberver(Observer observer) {
        int i = observers.indexOf(observer);
        if (i >= 0) {
            observers.remove(i);
        }
    }

    /**
     * 气象站得到更新的观测数据时，通知观察者
     */
    public void measurementChanged() {
        notifyObserver();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.tempterature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementChanged();
    }
}

/**
 * 抽象观察者
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
interface Observer {
    public void update(float temp, float humidity, float pressure);
}

/**
 * 显示接口
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
interface DisplayElement {
    public void display();
}

/**
 * 具体实现：观察者、显示接口
 *
 * @author dduan
 * @version 1.0
 * @date 2023/4/10
 */
class CurrentConditionsDisplay implements Observer, DisplayElement {
    private float temperature;
    private float humidity;
    private Subject weatherData;

    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        // 注册观察者
        weatherData.registerObserver(this);
    }

    @Override
    public void update(float temp, float humidity, float pressure) {
        this.temperature = temp;
        this.humidity = humidity;
        display();
    }

    @Override
    public void display() {
        System.out.println("Current conditions:" + temperature + "F degrees and " + humidity + "% humidity");
    }

}